package service

import (
	"batbat-backend/app/dao"
	"batbat-backend/app/model"
	"context"
	"github.com/gogf/gf/database/gdb"
	"github.com/gogf/gf/errors/gerror"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/os/gtime"
	"strings"
)

var GameReview = gameReviewService{}

type gameReviewService struct{}

// GetGameReviews 获取游戏评论
func (gr *gameReviewService) GetGameReviews(ctx context.Context, id uint, pn int, limit int) (
	resp *model.GameReviewApiGetGameReviewsResp, err error) {
	err = g.DB().Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
		var reviews []*model.GameReview
		// 获取游戏评论列表
		err := dao.GameReview.Ctx(ctx).TX(tx).
			Fields(dao.GameReview.Columns).
			Where(dao.GameReview.Columns.Gid, id).
			WhereNull(dao.GameReview.Columns.DeleteTime). // 获取未删除的游戏评论
			OrderDesc(dao.GameReview.Columns.UpdateTime).
			Page(pn, limit).
			Scan(&reviews)
		if err != nil {
			g.Log().Ctx(ctx).Errorf("查询数据库失败: %v", err)
			return gerror.New("获取评论失败")
		}
		if reviews == nil || len(reviews) == 0 {
			if pn == 1 {
				resp = &model.GameReviewApiGetGameReviewsResp{
					List:  make([]*model.GameReviewDetail, 0),
					Ratio: make([]*model.GameReviewRatio, 0),
				}
				return nil
			} else {
				return gerror.New("未查询到数据, 请校验参数`pn`或`limit`")
			}
		}
		// 查询数据量
		count, err := dao.GameReview.Ctx(ctx).TX(tx).Where(dao.GameReview.Columns.Gid, id).Count()
		if err != nil {
			g.Log().Ctx(ctx).Errorf("查询数据库失败: %v", err)
			return gerror.New("获取评论失败")
		}
		// 构造结果集
		resp = &model.GameReviewApiGetGameReviewsResp{
			Page: model.Page{
				Prev:  pn - 1,
				Count: count,
				Limit: limit,
			},
			List: make([]*model.GameReviewDetail, len(reviews)),
		}
		// 判断是否存在下一页
		if pn*limit < resp.Count {
			resp.Next = pn + 1
		} else {
			resp.Next = 0
		}
		for i, r := range reviews {
			// 获取评论详情
			re, err := gr.getGameReviewRespHelper(ctx, r)
			if err != nil {
				g.Log().Ctx(ctx).Errorf("查询数据库失败: %v", err)
				return gerror.New("获取评论失败")
			}
			// 初始化当前评论响应
			resp.List[i] = &model.GameReviewDetail{Review: re}
			// 查询评论的用户
			err = dao.User.Ctx(ctx).TX(tx).
				Fields(dao.User.Columns).
				WherePri(reviews[i].Uid).
				Scan(&resp.List[i].User)
			if err != nil {
				g.Log().Ctx(ctx).Errorf("查询数据库失败: %v", err)
				return gerror.New("获取评论失败")
			}
		}
		// 计算评论评分占比
		resp.Ratio = make([]*model.GameReviewRatio, 0)
		if resp.Count != 0 {
			err := dao.GameReview.Ctx(ctx).TX(tx).
				Fields(dao.GameReview.Columns.Score, "FORMAT((COUNT(1)/ ? * 100), 2) AS ratio").Args(resp.Count).
				Where(dao.GameReview.Columns.Gid, id).
				Group(dao.GameReview.Columns.Score).
				Order(dao.GameReview.Columns.Score).
				Scan(&resp.Ratio)
			if err != nil {
				g.Log().Ctx(ctx).Errorf("查询数据库失败: %v", err)
				return gerror.New("获取评论失败")
			}
		}
		return nil
	})
	return resp, err
}

// getGameReviewRespHelper 获取评论详情的帮助方法
func (gr *gameReviewService) getGameReviewRespHelper(ctx context.Context, review *model.GameReview) (
	resp *model.GameReviewResp, err error) {
	err = g.DB().Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
		// 获取评论的点赞数
		like, err := dao.GameReviewLike.Ctx(ctx).TX(tx).
			Where(dao.GameReviewLike.Columns.Grid, review.Id).
			WhereNull(dao.GameReviewLike.Columns.DeleteTime).
			Count()
		if err != nil {
			return err
		}
		review.Like += uint(like)
		// 构造响应
		resp = &model.GameReviewResp{
			GameReview: review,
			IsUp:       false, // 默认未点赞
		}
		// 判断用户是否点赞
		if user, ok := ctx.Value(model.UserContextKey).(*model.UserContext); ok {
			count, err := dao.GameReviewLike.Ctx(ctx).TX(tx).
				Where(dao.GameReviewLike.Columns.Grid, review.Id).
				Where(dao.GameReviewLike.Columns.Uid, user.Id).
				WhereNull(dao.GameReviewLike.Columns.DeleteTime).
				Count()
			if err != nil {
				return err
			}
			if count > 0 {
				resp.IsUp = true
			}
		}
		return nil
	})
	return resp, err
}

// NewGameReview 新增对游戏的评论
func (gr *gameReviewService) NewGameReview(ctx context.Context, id uint, content string, score int) (review *model.GameReviewDetail, err error) {
	// 获取用户上下文
	user, ok := ctx.Value(model.UserContextKey).(*model.UserContext)
	if !ok {
		g.Log().Ctx(ctx).Errorf("发生错误, 无法获取用户对象, 请检查是否配置了Login中间件以及NeedLogin中间件")
		return nil, gerror.New("评论失败")
	}
	err = g.DB().Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
		// 在插入新的记录前查看是否有已经存在的记录, 这里不会查询delete_time不为null的数据, 方便后续节省空间
		count, err := dao.GameReview.Ctx(ctx).TX(tx).
			Where(dao.GameReview.Columns.Gid, id).
			Where(dao.GameReview.Columns.Uid, user.Id).
			WhereNull(dao.GameReview.Columns.DeleteTime).
			Count()
		if err != nil {
			g.Log().Ctx(ctx).Errorf("查询数据库发生错误: %v", err)
			return gerror.New("评论失败")
		}
		if count > 0 {
			return gerror.New("已经做出过评论, 无法再次评论")
		}

		// 向数据库中插入记录, 使用save方法可以在唯一键重复的时候进行更新, 节约空间
		r := &model.GameReview{
			Gid:        id,
			Uid:        user.Id,
			CreateTime: gtime.Now(),
			UpdateTime: gtime.Now(),
			DeleteTime: nil,
			Content:    strings.ReplaceAll(content, "\n", "<br>"), // 转为html内容
			RawContent: content,
			Score:      score,
		}
		_, err = dao.GameReview.Ctx(ctx).TX(tx).
			FieldsEx(dao.GameReview.Columns.Id).
			Save(r)
		if err != nil {
			g.Log().Ctx(ctx).Errorf("插入数据库失败: %v", err)
			return gerror.New("评论失败")
		}

		// 构造响应
		review = &model.GameReviewDetail{
			User: user,
		}
		// 查询评论
		err = dao.GameReview.Ctx(ctx).TX(tx).
			Fields(dao.GameReview.Columns).
			Where(dao.GameReview.Columns.Gid, id).
			Where(dao.GameReview.Columns.Uid, user.Id).
			Scan(&review.Review)
		if err != nil {
			g.Log().Ctx(ctx).Errorf("查询数据库失败: %v", err)
			return gerror.New("评论失败")
		}
		return nil
	})
	return review, err
}

// DeleteGameReview 删除对游戏的评论
func (gr *gameReviewService) DeleteGameReview(ctx context.Context, uid uint, gid uint) error {
	return g.DB().Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
		var review *model.GameReview
		// 查看是否存在该评论
		err := dao.GameReview.Ctx(ctx).TX(tx).
			Where(dao.GameReview.Columns.Uid, uid).
			Where(dao.GameReview.Columns.Gid, gid).
			WhereNull(dao.GameReview.Columns.DeleteTime).
			Scan(&review)
		if err != nil {
			g.Log().Ctx(ctx).Errorf("查询数据库失败: %v", err)
			return gerror.New("删除评论失败")
		}
		if review == nil {
			return gerror.New("不存在该评论")
		}
		// 删除评论
		data := g.MapStrAny{dao.GameReview.Columns.DeleteTime: gtime.Now()}
		_, err = dao.GameReview.Ctx(ctx).TX(tx).WherePri(review.Id).Update(data)
		if err != nil {
			g.Log().Ctx(ctx).Errorf("修改数据库失败: %v", err)
			return gerror.New("删除评论失败")
		}
		return nil
	})
}

// Up 给评论点赞
func (gr *gameReviewService) Up(ctx context.Context, gid uint, rid uint) error {
	return g.DB().Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
		// 获取用户
		user, ok := ctx.Value(model.UserContextKey).(*model.UserContext)
		if !ok {
			g.Log().Ctx(ctx).Error("获取用户上下文对象失败")
			return gerror.New("点赞失败")
		}
		// 查看评论是否存在
		count, err := dao.GameReview.Ctx(ctx).TX(tx).
			WherePri(rid).
			Where(dao.GameReview.Columns.Gid, gid).
			WhereNull(dao.GameReview.Columns.DeleteTime).
			Count()
		if err != nil {
			g.Log().Ctx(ctx).Errorf("查找数据库发生错误: %v", err)
			return gerror.New("点赞失败")
		}
		if count == 0 {
			return gerror.New("评论不存在")
		}
		// 点赞评论
		_, err = dao.GameReviewLike.Ctx(ctx).TX(tx).
			Fields(dao.GameReviewLike.Columns.Uid,
				dao.GameReviewLike.Columns.Grid,
				dao.GameReviewLike.Columns.DeleteTime).
			Save(model.GameReviewLike{Grid: rid, Uid: user.Id, DeleteTime: nil})
		if err != nil {
			g.Log().Ctx(ctx).Errorf("插入数据库发生错误: %v", err)
			return gerror.New("点赞失败")
		}
		return nil
	})
}

// UnUp 取消给评论的点赞
func (gr *gameReviewService) UnUp(ctx context.Context, gid uint, rid uint) error {
	return g.DB().Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
		// 获取用户
		user, ok := ctx.Value(model.UserContextKey).(*model.UserContext)
		if !ok {
			g.Log().Ctx(ctx).Error("获取用户上下文对象失败")
			return gerror.New("取消点赞失败")
		}
		// 查看评论是否存在
		count, err := dao.GameReview.Ctx(ctx).TX(tx).
			WherePri(rid).
			Where(dao.GameReview.Columns.Gid, gid).
			WhereNull(dao.GameReview.Columns.DeleteTime).
			Count()
		if err != nil {
			g.Log().Ctx(ctx).Errorf("查找数据库发生错误: %v", err)
			return gerror.New("取消点赞失败")
		}
		if count == 0 {
			return gerror.New("评论不存在")
		}
		// 查看是否点赞过该评论
		count, err = dao.GameReviewLike.Ctx(ctx).TX(tx).
			Where(dao.GameReviewLike.Columns.Grid, rid).
			Where(dao.GameReviewLike.Columns.Uid, user.Id).
			WhereNull(dao.GameReviewLike.Columns.DeleteTime).
			Count()
		if err != nil {
			g.Log().Ctx(ctx).Errorf("查找数据库发生错误: %v", err)
			return gerror.New("取消点赞失败")
		}
		if count == 0 {
			return gerror.New("还未点赞过该评论")
		}
		// 取消点赞
		_, err = dao.GameReviewLike.Ctx(ctx).TX(tx).
			Fields(dao.GameReviewLike.Columns.Uid,
				dao.GameReviewLike.Columns.Grid,
				dao.GameReviewLike.Columns.DeleteTime).
			Save(model.GameReviewLike{Grid: rid, Uid: user.Id, DeleteTime: gtime.Now()})
		if err != nil {
			g.Log().Ctx(ctx).Errorf("插入数据库发生错误: %v", err)
			return gerror.New("取消点赞失败")
		}
		return nil
	})
}
